using System;
using System.ServiceModel.Channels;

namespace WcfExtensions.ServiceModel.Channels
{
    internal sealed class CompressionChannelListener<TChannelShape> : ChannelListenerBase<TChannelShape>
        where TChannelShape : class, IChannel
    {
        private readonly CompressionAlgorithm _compressionAlgorithm;
        private readonly CompressionLevel _compressionLevel;
        private readonly IChannelListener<TChannelShape> _innerListener;

        public CompressionChannelListener(CompressionBindingElement bindingElement, BindingContext context)
            : base(context.Binding)
        {
            _compressionLevel = bindingElement.Level;
            _compressionAlgorithm = bindingElement.Algorithm;
            _innerListener = context.BuildInnerChannelListener<TChannelShape>();
        }

        public CompressionLevel Level
        {
            get { return _compressionLevel; }
        }

        public CompressionAlgorithm Algorithm
        {
            get { return _compressionAlgorithm; }
        }

        public override Uri Uri
        {
            get { return _innerListener.Uri; }
        }


        private TChannelShape SetChannel(TChannelShape innerChannel)
        {
            if (innerChannel == null)
            {
                throw new ArgumentNullException("innerChannel");
            }

            if (typeof (TChannelShape) == typeof (IInputChannel))
            {
                return
                    (TChannelShape)
                    (object) new CompressionInputChannel<IInputChannel>(this, (IInputChannel) innerChannel);
            }
            if (typeof (TChannelShape) == typeof (IReplyChannel))
            {
                return (TChannelShape) (object) new CompressionReplyChannel(this, (IReplyChannel) innerChannel);
            }
            if (typeof (TChannelShape) == typeof (IInputSessionChannel))
            {
                return
                    (TChannelShape)
                    (object) new CompressionInputSessionChannel(this, (IInputSessionChannel) innerChannel);
            }
            if (typeof (TChannelShape) == typeof (IReplySessionChannel))
            {
                return
                    (TChannelShape)
                    (object) new CompressionReplySessionChannel(this, (IReplySessionChannel) innerChannel);
            }
            if (typeof (TChannelShape) == typeof (IDuplexChannel))
            {
                return
                    (TChannelShape)
                    (object) new CompressionDuplexChannel<IDuplexChannel>(this, (IDuplexChannel) innerChannel);
            }
            if (typeof (TChannelShape) == typeof (IDuplexSessionChannel))
            {
                return
                    (TChannelShape)
                    (object) new CompressionDuplexSessionChannel(this, (IDuplexSessionChannel) innerChannel);
            }

            throw new InvalidOperationException("Unsupported channel type");
        }

        protected override TChannelShape OnAcceptChannel(TimeSpan timeout)
        {
            TChannelShape innerChannel = _innerListener.AcceptChannel(timeout);
            if (innerChannel != null)
            {
                return SetChannel(innerChannel);
            }
            return null;
        }

        protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return _innerListener.BeginAcceptChannel(timeout, callback, state);
        }

        protected override TChannelShape OnEndAcceptChannel(IAsyncResult result)
        {
            TChannelShape innerChannel = _innerListener.EndAcceptChannel(result);
            if (innerChannel == null)
                return null;

            return SetChannel(innerChannel);
        }

        protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return _innerListener.BeginWaitForChannel(timeout, callback, state);
        }

        protected override bool OnEndWaitForChannel(IAsyncResult result)
        {
            return _innerListener.EndWaitForChannel(result);
        }

        protected override bool OnWaitForChannel(TimeSpan timeout)
        {
            return _innerListener.WaitForChannel(timeout);
        }

        protected override void OnAbort()
        {
            _innerListener.Abort();
        }

        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return _innerListener.BeginClose(timeout, callback, state);
        }

        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return _innerListener.BeginOpen(timeout, callback, state);
        }

        protected override void OnClose(TimeSpan timeout)
        {
            _innerListener.Close(timeout);
        }

        protected override void OnEndClose(IAsyncResult result)
        {
            _innerListener.EndClose(result);
        }

        protected override void OnEndOpen(IAsyncResult result)
        {
            _innerListener.EndOpen(result);
        }

        protected override void OnOpen(TimeSpan timeout)
        {
            _innerListener.Open(timeout);
        }

        public override T GetProperty<T>()
        {
            return _innerListener.GetProperty<T>();
        }
    }
}